/*
 *  Options.h
 *  vorpal
 *
 *  Created by rwebb on 19/02/09.
 *
 */

#ifndef OPTIONS_H
#define OPTIONS_H

#include <iostream>
#include <fstream>
#include <map>
#include <vector>
#include <string>
#include "Common.h"

/**
 * \brief Handles the arguments given to vorpal in the command line
 */
class Options{
public:
	Options(const char *headerStr = "usage: ") : header(headerStr){ 
	
	}
	virtual ~Options(){ }
	
	virtual void Define(bool readsValue, const char *label, const char *abbreviation, const char *helpStr, const char *defaultValue = "", bool absorbRemaining = false){
		string key(label);
		options[key] = Option(readsValue, label, abbreviation, helpStr, defaultValue, absorbRemaining);
	}
	
	/// returns error string
	virtual const char *Read(int numArgs, const char *arg[]){
		loop(i, 1, numArgs){
			bool found = false;
			for(map< string, Option >::iterator it = options.begin(); it != options.end(); ++it){
				if(it->second.abbreviation == arg[i] || it->second.label == arg[i]){
					string key(it->second.label);
					if(it->second.readsValue){
						if(i + 1 >= numArgs){
							cout << it->second.label << " needs a value\n";
							HelpString();
							Require(false);
						}
						values[key] = string(arg[i + 1]);
						i += 1;
					}
					else{
						values[key] = string(arg[i]);
					}
					if(it->second.absorbRemaining){
						loop(remain, i + (it->second.readsValue ? 2 : 1), numArgs){
							remaining.push_back(string(arg[remain]));
						}
						return 0;
					}					
					found = true;
					break;
				}
			}
			if(!found){
				if(arg[i][0] == '-'){
					return arg[i];
				}
				extra.push_back(string(arg[i]));
			}
		}
		return 0;
	}
	
	virtual string HelpString(){
		ostringstream s;
		s << header << endl;
		for(map< string, Option >::iterator it = options.begin(); it != options.end(); ++it){
			int len = it->second.label.length();
			loop(i, 0, 18 - len){
				s << " ";
			}
			s << it->second.label << " | " << it->second.abbreviation << " : " << it->second.helpStr << endl;
			if(it->second.defaultValue != ""){
				s << "\t( default = " << it->second.defaultValue << " )";
			}
		}
		return s.str();
	}
	
	bool Has(const char *label){
		string key(label);
		return values.find(key) != values.end() || (options.find(key) != options.end() && options[key].defaultValue != "");
	}
	
	string Get(const char *label){
		string key(label);
		if(values.find(key) != values.end()){
			return values[key];
		}
		if(options.find(key) != options.end()){
			return options[key].defaultValue;
		}
		Require(false);
		return string();
	}
	
	const string &Extra(uint32_t index) const{
		Assert(index < extra.size());
		return extra[index];
	}
	
	uint32_t NumExtras() const{ return extra.size(); }

	const string &Remaining(uint32_t index) const{
		Assert(index < remaining.size());
		return remaining[index];
	}
	
	uint32_t NumRemaining() const{ return remaining.size(); }
	
protected:
	struct Option{
		Option(bool readsValue, const char *label, const char *abbreviation, const char *helpStr, const char *defaultValue, bool absorbRemaining):
		readsValue(readsValue), label(label), abbreviation(abbreviation), helpStr(helpStr), defaultValue(defaultValue), absorbRemaining(absorbRemaining)
		{ }
		
		Option(){ }
		
		bool readsValue;
		string label;
		string abbreviation;
		string helpStr;
		string defaultValue;
		bool absorbRemaining;
	};
	string header;
	map< string, Option > options;
	map< string, string > values;
	vector< string > extra;
	vector< string > remaining;
};


#endif
